#!/usr/bin/env python

# Big block of hackish code. Update description later.

# -------
# IMPORTS
# -------
import direct.directbase.DirectStart
from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionSphere,CollisionRay
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import Vec3,Vec4,BitMask32
from pandac.PandaModules import MovieTexture
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.interval.IntervalGlobal import *
import random, sys, os, math, time

# ------------------------
class World(DirectObject):
# ------------------------

    # ---------------------------
    # BEGINNING OF INITIALIZATION
    # ---------------------------
    def __init__(self):

        # ------------------------------------------
        # WORLD CONSTANTS DECLARATION AND DEFINITION
        # ------------------------------------------

        # is Ralph moving at the moment? Used for animation?
        self.isMoving = False

        # For Determining Time Between Frames
        self.startTime = 0
        self.lastTime = globalClock.getFrameTime()
        self.dTime = 0

        # Movement Speed
        self.WALKSPEED = 25
        self.RUNSPEED = 50

        # Jumping/Falling Constants
        self.GRAVITY = 1.5
        self.JUMPVECTOR = 20
        self.MAXDOWNSPEED = -100
        self.upSpeed = 0
        self.downForce = 0
        self.ZBOUND = -15

        # --------------------------
        # KEY MAPPING AND ASSIGNMENT
        # --------------------------

        self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, 
                                "turned":0, "running":0, "jumping":0, "grounded":0}
        self.accept("escape", sys.exit)
        self.accept("a", self.setKey, ["running",1])
        self.accept("mouse1", self.setKey, ["turned",1])
        self.accept("s", self.setKey, ["backward",1])
        self.accept("w", self.setKey, ["forward",1])
        self.accept("c", self.toggleCameraMode)
        self.accept("v", self.toggleViewCollisions)
        self.accept("space", self.setKey, ["jumping",1])
        self.accept("a-up", self.setKey, ["running",0])
        self.accept("w-up", self.setKey, ["forward",0])
        self.accept("mouse1-up", self.setKey, ["turned",0])
        self.accept("s-up", self.setKey, ["backward",0])

        # Camera movement functions
        taskMgr.add(self.move,"moveTask")
        taskMgr.add(self.cameraTask,"cameraTask")
        
        # -------------------
        # ENVIRONMENT LOADING
        # -------------------

        # Makes the non-textured world environment black (aka "clear" color)
        base.win.setClearColor(Vec4(0,0,0,1))

        # Decide which world level to load
        #self.environ = loader.loadModel("../models/ground/level_1-1") # LEVEL 1-1
        self.environ = loader.loadModel("../models/one-two/level_1-2") # LEVEL 1-2
        self.environ.reparentTo(render)
        self.environ.setPos(0,0,0)
        
        # -------------
        # ACTOR LOADING
        # -------------

        # Create and load the main character (at this moment, Ralph)
        self.ralph = Actor("../actors/ralph/ralph",
                                 {"run":"../actors/ralph/ralph-run", 
                                  "walk":"../actors/ralph/ralph-walk"})
        self.ralph.reparentTo(render) # renders main character
        self.ralph.setScale(.5) # scales character, if necessary
        self.ralph.setPos(0, 5, 6) # set position in current level
        self.ralph.setHpr(180.0, 0, 0) # infinite wave of light, set directionary on character

        # ------------------------------
        # CAMERA LOADING AND POSITIONING
        # ------------------------------

        # Set up the camera
        base.disableMouse() # I'm assuming this disables direct mouse tracking
        base.camera.setPos(self.ralph.getX(),self.ralph.getY(),self.ralph.getZ()+1) # position
        self.ralph.setHpr(self.ralph.getH(), self.ralph.getP(), self.ralph.getR()) # lighting

        # Where the camera floats above the grove
        self.CAMERA_Z_OFFSET = 0 # self.CAMZ
        self.CAMDISTANCE = 30 # distance for 3rd person
        self.YROTCONST = 60.0
        self.XROTCONST = -60.0
        self.CAM_ZEN_SCALE = math.pi / 8.0
        self.CAM_ZEN_OFFSET = - 3.0 * math.pi / 8.0
        self.CAM_AZ_SCALE = math.pi / 8.0
        self.CAM_AZ_OFFSET = 5.0 * math.pi / 8.0

        # First or third person perspective?
        self.CAMSTYLE = 1 # 1 = first person, 2 = third person

        # -----------------
        # LIGHTING HANDLING
        # -----------------

        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(.3, .3, .3, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(-5, -5, -5))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
        render.setLight(render.attachNewNode(ambientLight))
        render.setLight(render.attachNewNode(directionalLight))

        # ------------------------------------------
        # COLLISION HANDLING - ACTOR AND ENVIRONMENT
        # ------------------------------------------

        # View Collisions? (debugging parameter)
        self.VIEWCOL = 0

        # Does the actual work of checking all solid objects for collisions
        self.cTrav = CollisionTraverser()

        # Ground Level Sphere (on Ralph)
        self.ralphGroundSphereHandler = CollisionHandlerQueue()
        self.ralphGroundSphereColNp = self.setupSphereCollider(self.ralph, 0, 0, 2, 1.9, 
                'ralphGroundSphere', self.ralphGroundSphereHandler, True)

        # Head Level Sphere (on Ralph)
        self.ralphHeadSphereHandler = CollisionHandlerQueue()
        self.ralphHeadSphereColNp = self.setupSphereCollider(self.ralph, 0, 0, 3.5, 1, 
                'ralphHeadSphere', self.ralphHeadSphereHandler, True)

        # Environment and Actor Ground Collision Rays
        self.ralphGroundRay = CollisionRay()
        self.ralphGroundRay.setOrigin(0,0,1000)
        self.ralphGroundRay.setDirection(0,0,-1)
        self.ralphGroundCol = CollisionNode('ralphRay')
        self.ralphGroundCol.addSolid(self.ralphGroundRay)
        self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
        self.ralphGroundRayHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundRayHandler)

        # Renders actual collisions (debug) -- COMMENT THE BELOW LINE TO REMOVE     
        self.cTrav.showCollisions(render) 

        # ---------------------
        # ONSCREEN TEXT LOADING
        # ---------------------
        
        # Load Debugging Instructions
        self.instLoc = [0.90, 0.85, 0.80, 0.75, 0.70, 0.65]
        self.instructions = [None, None, None, None, None, None]
        self.setInst(0, "[GAME STATE]")
        self.setInst(1, "[UPSPEED]")
        self.setInst(2, "[JUMPING?]")
        self.setInst(3, "[CAM AZ ZEN]")
        self.setInst(4, "[CAM X Y Z]")
        self.setInst(5, "Camera: first person")

	    # Initialize Death Text for Later Use
        self.deathText = self.addCenteredRedTitle("GAME OVER")
        self.deathText.hide(BitMask32.allOn())
        self.deathText2 = self.addCenteredRedSubtitle("Press ESC to Exit")
        self.deathText2.hide(BitMask32.allOn())

        # ------------------------------
        # SOUND EFFECT AND MUSIC LOADING
        # ------------------------------

        self.jumpSound = base.loadMusic("../sounds/effects/mario_jump.wav")
        self.coinSound = base.loadMusic("../sounds/effects/coin.wav")
        self.breakBlockSound = base.loadMusic("../sounds/effects/smb_breakblock.wav")
        self.fireballSound = base.loadMusic("../sounds/effects/fireball.wav")
        self.deathSound = base.loadMusic("../sounds/effects/death.wav")
        self.gameoverSound = base.loadMusic("../sounds/effects/game_over.wav")
        self.winStageSound = base.loadMusic("../sounds/effects/win_stage.wav")

        #-----------------------
        # CLIPPING PLANE LOADING
        #-----------------------

        self.initClippingPlane()

        # -------------
        # ENEMY LOADING
        # -------------

        self.goombae = {} # a record is: {int: ( eggmodel, direction, alive )}, 
                          # where direction = -1 for "toward start"
                          # where alive = True for yes, False for no
        self.koopae = {}  # a record is: {int: ( (koopamodel, shellmodel), direction, alive )},
                          # where direction = -1 for "toward start",
                          # where status = 1 for well, 0 for shell, -1 for dead
        self.enemies = (self.goombae, self.koopae)
        self.goombaSX = .25
        self.goombaSY = .25
        self.goombaSZ = .25

        #Load the Goombae
        self.goombae[0] = (loader.loadModel("../actors/goomba/goomba"), -1, True)
        enemy = self.goombae[0][0]
        enemy.reparentTo(render)
        enemy.setPos(0, 32, 1)
        enemy.setScale(self.goombaSX, self.goombaSY, self.goombaSZ)
        
        #Load the Koopae
        self.koopae[0] = ((loader.loadModel("../actors/koopa/koopa"), 
                loader.loadModel("../actors/koopa/shell")), -1, 1)
        shell = self.koopae[0][0][1]
        shell.reparentTo(render)
        koopa = self.koopae[0][0][0]
        koopa.reparentTo(shell)
        shell.setPos(0, 64, 1)
        shell.setScale(-1*self.goombaSX, self.goombaSY+.1, self.goombaSZ)
        koopa.setPos(0, 0, 0)
        koopa.setScale(-1, 1, 1)

        # define each goomba's movement
        for key, enemyRecord in self.enemies[0].items() :
            #define first interval
            Sequence(LerpPosInterval(enemyRecord[0], 1, 
                    self.goombaTarget(enemyRecord, enemyRecord[0].getPos())),
                    Func(self.nextGoombaMove, enemyRecord, self.goombaTarget(enemyRecord,
                    enemyRecord[0].getPos()))).start()
                     
        # define each koopa's movement
        # ... and nothing is here yet ...

        # ------------------------
        # ENEMY COLLISION HANDLING
        # ------------------------

        self.goombaSphereHandler = CollisionHandlerQueue()
        # Single Goomba right now - should be a LIST
        self.goombaSphereColNp = self.setupSphereCollider(self.enemies[0][0][0], 0, 0, 4, 6,
                'goombaSphere'+'#0', self.goombaSphereHandler, False)
        
        self.koopaSphereHandler = CollisionHandlerQueue()
        # Single Koopa right now - should be a LIST
        # the following line attaches sphere collision to shell nodePath
        self.koopaSphereColNp = self.setupSphereCollider(self.enemies[1][0][0][1], 0, 0, 4, 6, 
                'koopaSphere'+'#0', self.koopaSphereHandler, False)

    # ------------------------- #
    #   END OF INITIALIZATION   #
    # ------------------------- #


    # --------------------------------
    # PLACING TEXT ON SCREEN FUNCTIONS
    # --------------------------------

    # ----------------------------------
    def addInstructions(self, pos, msg):
    # ----------------------------------
        return OnscreenText(text=msg, style=1, fg=(1,1,1,1), pos=(-1.3, pos), align=TextNode.ALeft, scale = .05)

    # --------------------------
    def setInst(self, idx, msg):
    # --------------------------
        if idx >= len(self.instLoc):
            print "instLoc OOB: {0}".format(idx)
            return
        if idx >= len(self.instructions):
            print "inst OOB: {0}".format(idx)
            return
        if self.instructions[idx] is not None:
            self.instructions[idx].destroy()
        self.instructions[idx] = self.addInstructions(self.instLoc[idx], msg)

    # ---------------------------------
    def addCenteredRedTitle(self, msg):
    # ---------------------------------
        return OnscreenText(text=msg, style=3, fg=(1,0,0,1), pos=(0.0, 0.0), align=TextNode.ACenter, scale=.25)

    # ------------------------------------
    def addCenteredRedSubtitle(self, msg):
    # ------------------------------------
        return OnscreenText(text=msg, style=3, fg=(1,0,0,1), pos=(0.0, -0.1), align=TextNode.ACenter, scale=.05)

    # -----------------------------
    def toggleViewCollisions(self):
    # -----------------------------
        if (self.VIEWCOL == 1):
            self.VIEWCOL = 0
            self.ralphGroundSphereColNp.hide()
            self.ralphHeadSphereColNp.hide()
            self.goombaSphereColNp.hide()
            self.koopaSphereColNp.hide()
            self.ralphGroundColNp.hide()
        else:
            self.VIEWCOL = 1
            self.ralphGroundSphereColNp.show()
            self.ralphHeadSphereColNp.show()
            self.goombaSphereColNp.show()
            self.koopaSphereColNp.hide()
            self.ralphGroundColNp.show()

    # -------------------------
    def toggleCameraMode(self):
    # -------------------------
        if (self.CAMSTYLE == 1):
            self.CAMSTYLE = 2
            self.setInst(5, "Camera: third person")
        else:
            self.CAMSTYLE = 1
            self.setInst(5, "Camera: first person")

    # -----------------------------------------------------------------------
    def setupSphereCollider(self, target, x, y, z, r, name, handler, isFrom):
    # -----------------------------------------------------------------------
        sphere = CollisionSphere(x, y, z, r)
        sphereCol = CollisionNode(name)
        sphereCol.addSolid(sphere)
        if isFrom:
            sphereCol.setFromCollideMask(BitMask32.bit(0))
            sphereCol.setIntoCollideMask(BitMask32.allOff())
        else:
            sphereCol.setFromCollideMask(BitMask32.allOff())
            sphereCol.setIntoCollideMask(BitMask32.bit(0))
        np = target.attachNewNode(sphereCol)
        self.cTrav.addCollider(np, handler)
        return np

    """ Records the state of the arrow keys """
    # ---------------------------
    def setKey(self, key, value):
    # ---------------------------
        self.keyMap[key] = value
    
    """ Task to control looking up and down with the mouse """
    # ------------------------
    def cameraTask(self,task):
    # ------------------------
        if (self.CAMSTYLE == 1):
            # first person
            try: 
               # Get mouse coordinates from mouse watcher 
                if ( base.mouseWatcherNode.hasMouse() ):
                    yPos=base.mouseWatcherNode.getMouseY()
                    xPos=base.mouseWatcherNode.getMouseX()
                    camPitch = yPos * self.YROTCONST
                    camYaw = xPos * self.XROTCONST + self.ralph.getH() - 180.0
                    camRoll = base.camera.getR()
                    base.camera.setHpr(camYaw, camPitch, camRoll)
                    base.win.movePointer(0, center[0], center[1]) 
                
            except: 
                # If mouse goes outside window, catch the 
                #    exception here and pass gracefully. 
                pass 
            
            # always put camera where Ralph is
            base.camera.setPos(self.ralph.getX(), self.ralph.getY(), self.ralph.getZ()+4)
        elif (self.CAMSTYLE == 2):
            # third person
            try: 
               # Get mouse coordinates from mouse watcher 
                if ( base.mouseWatcherNode.hasMouse() ):
                    yPos=base.mouseWatcherNode.getMouseY()
                    xPos=base.mouseWatcherNode.getMouseX()
                    camAz = xPos * self.CAM_AZ_SCALE + self.CAM_AZ_OFFSET
                    camZen = yPos * self.CAM_ZEN_SCALE + self.CAM_ZEN_OFFSET
                    self.setInst(3, "cam Az,Zen: {0}, {1}".format(camAz, camZen))
                    camX = self.CAMDISTANCE * math.sin(camZen) * math.cos(camAz)
                    camY = self.CAMDISTANCE * math.sin(camZen) * math.sin(camAz)
                    camZ = self.CAMDISTANCE * math.cos(camZen)
                    self.setInst(4, "cam XYZ: {0}, {1}".format(camX, camY,camZ))
                    base.camera.setPos(self.ralph.getX() + camX,
                            self.ralph.getY() + camY, self.ralph.getZ() + camZ)
                    base.camera.lookAt(self.ralph)
                    base.win.movePointer(0, center[0], center[1]) 
                
            except: 
                # If mouse goes outside window, catch the 
                #    exception here and pass gracefully. 
                pass 
           
        return task.cont

    """ Accepts arrow keys to move either the player or the menu cursor,
        Also deals with grid checking and collision detection """
    # -------------------
    def move(self, task):
    # -------------------

        # Get time delta
        # NOTE: this ends up being equal to globalClock.getDt() but
        # for mysterious unknown reasons if I try to use that in the
        # falling code, everything gets screwed up. -MDD
        curTime = globalClock.getFrameTime()
        self.dTime = curTime - self.lastTime
        self.lastTime = curTime


        # save ralph's initial position so that we can restore it,
        # in case he falls off the map or runs into something.
        startpos = self.ralph.getPos()

        # Are we walking or running?
        if (self.keyMap["running"]!=0):
            movespeed = self.RUNSPEED
        else:
            movespeed = self.WALKSPEED

        # point ralph in the right direction
        if (self.keyMap["turned"]!=0):
            self.ralph.setHpr(0.0, 0, 0)
        else:
            self.ralph.setHpr(180.0, 0, 0)

        # If a move-key is pressed, move ralph in the specified direction.
        if (self.keyMap["left"]!=0):
            self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
        if (self.keyMap["right"]!=0):
            self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
        if (self.keyMap["forward"]!=0):
            self.ralph.setY(self.ralph, -movespeed * globalClock.getDt())
            self.moveClippingPlane()
        if (self.keyMap["backward"]!=0):
            self.ralph.setY(self.ralph, movespeed * globalClock.getDt())
            self.moveClippingPlane()

        # If ralph is moving, loop the run animation.
        # If he is standing still, stop the animation.
        if (self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0) or (self.keyMap["backward"]!=0):
            if self.isMoving is False:
                self.ralph.loop("run")
                self.isMoving = True
        else:
            if self.isMoving:
                self.ralph.stop()
                self.ralph.pose("walk",5)
                self.isMoving = False

        # Jumping
        if (self.keyMap["jumping"]!=0) and (self.keyMap["grounded"]!=0):
            self.setInst(2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            self.upSpeed = self.JUMPVECTOR
            self.setInst(1, "upSpeed: {0}".format(self.upSpeed))
            self.jumpSound.play()

        # Now check for collisions.
        self.cTrav.traverse(render)
  
        # assume Ralph is grounded, unless we find otherwise 
        # in the next bit of code
        self.setKey("grounded", 0)

        # Adjust ralph's Z coordinate.  If ralph's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.
        if (self.keyMap["jumping"]==0):
            entries = []
            for i in range(self.ralphGroundRayHandler.getNumEntries()):
                entry = self.ralphGroundRayHandler.getEntry(i)
                entries.append(entry)
            entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                         x.getSurfacePoint(render).getZ()))
            if (len(entries)>0):
                # find ground under Ralph
                for i in range(self.ralphGroundRayHandler.getNumEntries()):
                    groundDiff = entries[i].getSurfacePoint(render).getZ() - self.ralph.getZ() 
                    if (groundDiff < 1.2) and (groundDiff > -1.2): 
                        # Ralph is close enough to the ground - lock him to it
                        self.ralph.setZ(entries[i].getSurfacePoint(render).getZ())
                        self.upSpeed = 0.0
                        self.setInst(1, "upSpeed: {0}".format(self.upSpeed))
                        self.downForce = 0.0
                        self.setKey("grounded", 1)
                        self.setInst(2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            else:
                self.setInst(0, "Out of bounds!")

        #
        # Sphere ground handler for wall collision
        #
        entries = []
        for i in range(self.ralphGroundSphereHandler.getNumEntries()):
            entry = self.ralphGroundSphereHandler.getEntry(i)
            entries.append(entry)
            if entry.hasInto() :
                nodePath = entry.getIntoNodePath()
                if "goomba" in nodePath.getName() :
                    goomba_index = int(nodePath.getName()[nodePath.getName().find('#')+1:])
                    print "hit goomba with Ralph at " + str(self.ralph.getZ()) + "; goomba at " + str(self.goombae[goomba_index][0].getZ())
                    
                    if self.ralph.getZ() > self.goombae[goomba_index][0].getZ():
                        print "Hit Goomba #" + str(goomba_index)
                        
                        if self.goombae[goomba_index][2] is True :
                            print "and he's alive"
                            #TODO bounce off goomba
                            nodePath.detachNode() #removes collision
                            self.goombae[goomba_index][0].detachNode() #removes render object
                            self.goombae[goomba_index] = (self.goombae[goomba_index][0]
                                    , self.goombae[goomba_index][1], False)                            
                        
                    else :
                        if self.goombae[goomba_index][2] is True:
                            print "hit by goomba"
                        #else:
                        #   we have a phantom Goomba
                elif "koopa" in nodePath.getName() :
                    koopa_index = int(nodePath.getName()[nodePath.getName().find('#')+1:])
                    print "hit koopa with Ralph at " + str(self.ralph.getZ()) + "; koopa at " + str(self.koopae[koopa_index][0][1].getZ())
                    
                    if self.ralph.getZ() > self.koopae[koopa_index][0][1].getZ():
                        print "Hit Koopa #" + str(koopa_index)
                        
                        if self.koopae[koopa_index][2] is 1 :
                            print "and he's alive and well"
                            #TODO bounce off koopa
                            #nodePath.detachNode() #removes collision
                            self.koopae[koopa_index][0][0].detachNode() #removes render object: body
                            self.koopae[koopa_index] = (self.koopae[koopa_index][0], 
                                    self.koopae[koopa_index][1], self.koopae[koopa_index][2]-1)
                        elif self.koopae[koopa_index][2] is 0:
                            print "and he's a shell of his former self"
                            nodePath.detachNode() #removes collision
                            self.koopae[koopa_index][0][1].detachNode() #removes render object: shell
                        #else:
                        #   we have a phantom Koopa
                    else :
                        if self.koopae[koopa_index][2] is True:
                            print "hit by koopa"
                        #else:
                        #   we have a phantom Koopa
                
        if (len(entries)>0): 
            # Ralph has hit an obstacle, don't let him move forward
            self.ralph.setPos(startpos)
            self.setInst(0, "Hit Obstacle")

        # Head sphere handler
        hitHead = 0
        entries = []
        for i in range(self.ralphHeadSphereHandler.getNumEntries()):
            entry = self.ralphHeadSphereHandler.getEntry(i)
            entries.append(entry)
            objY = entries[i].getSurfacePoint(render).getY()
            objZ = entries[i].getSurfacePoint(render).getZ()
            if (self.upSpeed > 0) and (objZ - self.ralph.getZ() > 0.0)\
            and (abs(objY - self.ralph.getY()) < 1.0):
                # the object was above Ralph; halt his upward velocity
                # HIT BLOCK NOISE -- TEMP -- FIXME
                self.coinSound.play()
                self.upSpeed = 0;
                self.setKey("grounded", 0)
                self.setInst(0, "Ungrounded")
                # make sure he doesn't get stuck in the block
                self.ralph.setZ(objZ - 1.5)
                hitHead = 1
                
        if (not hitHead) and (len(entries)>0): 
            # Ralph has hit an obstacle, don't let him move forward
            self.ralph.setPos(startpos)
            self.setInst(0, "Hit Obstacle")

        # gravity
        if (self.keyMap["grounded"] == 0):
            # Ralph is above the ground, make him fall
            self.ralph.setZ(self.ralph.getZ() + (self.upSpeed * self.dTime))
            self.downForce = self.downForce + (self.GRAVITY * self.dTime)
            self.upSpeed = self.upSpeed - self.downForce
            if (self.upSpeed < 0.0): self.setKey("jumping", 0)
            self.setInst(2, "JUMPING: {0}".format(self.keyMap["jumping"]))
            if (self.upSpeed < self.MAXDOWNSPEED): self.upSpeed = self.MAXDOWNSPEED
            self.setInst(1, "upSpeed: {0}".format(self.upSpeed))
            #self.setKey("grounded", 0)
            #self.setInst(0, "Grounded")
        
        # falling to death
        if (self.ralph.getZ() < self.ZBOUND):
            self.die() 
            return task.done
        else:
            return task.cont

    # --------------------------
    def initClippingPlane(self):
    # --------------------------
        # Clipping plane variables
        self.X_OFF = 2
        self.Y_OFF = 0
        self.Z_OFF = 10
        
        self.CPYAW = 0
        self.CPPITCH = 0
        self.CPROLL = 0

        self.VIDYAW = 0
        self.VIDPITCH = 0
        self.VIDROLL = 0

        self.CP_SX = 10
        self.CP_SY = 20
        self.CP_SZ = 18
        
        self.clipping_plane = loader.loadModel("../models/clipping-plane/clipping_plane.egg")
        self.clipping_plane.reparentTo(render)
        self.clipping_plane.setPos(self.ralph.getX() + self.X_OFF, self.ralph.getY()
                + self.Y_OFF, self.ralph.getZ() + self.Z_OFF)
        self.clipping_plane.setHpr(self.CPYAW, self.CPPITCH, self.CPROLL)
        self.clipping_plane.setScale(self.CP_SX, self.CP_SY, self.CP_SZ)

        self.tex0 = MovieTexture("movie0")
        #assert self.tex0.read("../models/video/horse_race.avi"), "Video 0 FAIL"
        self.tex1 = MovieTexture("movie1")
        #assert self.tex1.read("../models/video/lightning.avi"), "Video 1 FAIL"
        
        self.vid = loader.loadModel("../models/clipping-plane/video_screen.egg")
        self.vid.reparentTo(render)
        self.vid.setTexture(self.tex0, 1)
        self.vid.setPos(self.ralph.getX() + self.X_OFF, self.ralph.getY() 
                + self.Y_OFF, self.ralph.getZ() + self.Z_OFF)
        self.vid.setHpr(self.VIDYAW, self.VIDPITCH, self.VIDROLL)
        self.vid.setScale(self.CP_SX, self.CP_SY, self.CP_SZ)
    
    # --------------------------
    def moveClippingPlane(self):
    # --------------------------
        self.clipping_plane.setY(self.ralph.getY() + self.Y_OFF)
        self.vid.setY(self.ralph.getY() + self.Y_OFF)

    # ----------------------------
    def changeVideo(self, vidNum):
    # ----------------------------
        if vidNum == 0:
            self.vid.setTexture(self.tex0, 1)
        elif vidNum == 1:
            self.vid.setTexture(self.tex1, 1)
            
    """ Death screen """
    # ------------
    def die(self):
    # ------------
        self.deathText.show(BitMask32.allOn())
        self.deathText2.show(BitMask32.allOn())
        self.gameoverSound.play()
        #exit() # for now just end the program

    """ Enemy Movement """
    # ------------------------------------------
    def goombaTarget(self, enemyRecord, point3):
    # ------------------------------------------
        direction = enemyRecord[1]
        return ( point3[0], point3[1] + 2*direction, point3[2] )
    
    # -------------------------------------------------
    def nextGoombaMove(self, goombaRecord, moveTarget):
    # -------------------------------------------------
        newTarget = self.goombaTarget(goombaRecord, moveTarget)
        Sequence(LerpPosInterval(goombaRecord[0], 1, newTarget),
                 Func(self.nextGoombaMove, goombaRecord, newTarget)).start()
        #sequence.append(LerpPosInterval(goombaRecord[0], 3, self.goombaTarget(goombaRecord)))

if __name__ == "__main__":
    # Initialize the world
    w = World()
    # run the game
    run()


